package org.jeecg.modules.xxgx.fpgl.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.apache.shiro.SecurityUtils;
import org.jeecg.common.api.dto.message.MessageDTO;
import org.jeecg.common.api.vo.Result;
import org.jeecg.common.constant.CommonConstant;
import org.jeecg.common.constant.enums.MessageTypeEnum;
import org.jeecg.common.exception.JeecgBootException;
import org.jeecg.common.system.api.ISysBaseAPI;
import org.jeecg.common.system.vo.LoginUser;
import org.jeecg.modules.common.util.LoginUserUtil;
import org.jeecg.modules.innerApi.common.requestDto.CommonParam;
import org.jeecg.modules.innerApi.common.requestDto.FpzfParam;
import org.jeecg.modules.innerApi.common.requestDto.InvoiceParam;
import org.jeecg.modules.innerApi.common.requestDto.InvoicemxParam;
import org.jeecg.modules.innerApi.common.responseDto.FpzfRtn;
import org.jeecg.modules.innerApi.common.responseDto.InvoiceRtn;
import org.jeecg.modules.innerApi.leqiApi.service.ILeqiService;
import org.jeecg.modules.innerApi.zhiYunApi.service.IZhiyunSkfpService;
import org.jeecg.modules.innerApi.zhiYunSdApi.service.IZhiyunSdfpService;
import org.jeecg.modules.qyxx.entity.CsPtQyxx;
import org.jeecg.modules.task.entity.CsPtTask;
import org.jeecg.modules.task.entity.CsPtTaskmx;
import org.jeecg.modules.task.enums.TaskEnum;
import org.jeecg.modules.task.enums.TaskStateEnum;
import org.jeecg.modules.task.service.ICsPtTaskService;
import org.jeecg.modules.task.service.ICsPtTaskmxService;
import org.jeecg.modules.task.util.TaskWorkUtils;
import org.jeecg.modules.xxgx.common.enums.*;
import org.jeecg.modules.xxgx.common.util.FPUtil;
import org.jeecg.modules.xxgx.common.util.InvoiceConvertUtils;
import org.jeecg.modules.xxgx.common.util.InvoiceParamValid;
import org.jeecg.modules.xxgx.djgl.entity.CsXxDjtsjlDo;
import org.jeecg.modules.xxgx.djgl.entity.CsXxDkdj;
import org.jeecg.modules.xxgx.djgl.entity.CsXxDkdjCezs;
import org.jeecg.modules.xxgx.djgl.entity.CsXxDkdjExt;
import org.jeecg.modules.xxgx.djgl.entity.cezs.Cezs;
import org.jeecg.modules.xxgx.djgl.entity.tdyw.*;
import org.jeecg.modules.xxgx.djgl.enums.ZsfsEnum;
import org.jeecg.modules.xxgx.djgl.service.*;
import org.jeecg.modules.xxgx.djgl.vo.CsXxDkdjPage;
import org.jeecg.modules.xxgx.fpgl.dto.FpzfDto;
import org.jeecg.modules.xxgx.fpgl.entity.CsXxFpxx;
import org.jeecg.modules.xxgx.fpgl.entity.CsXxFpxxmx;
import org.jeecg.modules.xxgx.fpgl.event.BatchInvoiceEvent;
import org.jeecg.modules.xxgx.fpgl.event.FpDownloadEvent;
import org.jeecg.modules.xxgx.fpgl.event.vo.BatchInvoiceEventVo;
import org.jeecg.modules.xxgx.fpgl.event.vo.FpDownloadEventVo;
import org.jeecg.modules.xxgx.fpgl.param.BacthInvoiceParam;
import org.jeecg.modules.xxgx.fpgl.service.ICsXxFpxxService;
import org.jeecg.modules.xxgx.fpgl.service.ICsXxFpxxmxService;
import org.jeecg.modules.xxgx.fpgl.service.IFpkjglService;
import org.jeecg.modules.xxgx.fpgl.vo.FphcPLPage;
import org.jeecg.modules.xxgx.fpgl.vo.FphcPLRtn;
import org.jeecg.modules.xxgx.fpgl.vo.YkfpzfRtn;
import org.jeecg.modules.xxgx.fpht.event.FphtEvent;
import org.jeecg.modules.xxgx.fwqxe.entity.CsXxFwqxe;
import org.jeecg.modules.xxgx.fwqxe.service.ICsXxFwqxeService;
import org.jeecg.modules.xxgx.hzsqgl.entity.CsXxHzsqjs;
import org.jeecg.modules.xxgx.hzsqgl.service.ICsXxHzsqjsService;
import org.jeecg.modules.xxgx.kpzdgl.entity.CsXxKpzd;
import org.jeecg.modules.xxgx.kpzdgl.service.ICsXxKpzdService;
import org.jeecg.modules.xxgx.spgl.entity.CsXxProduct;
import org.jeecg.modules.xxgx.spgl.service.ICsXxProductService;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.TransactionTemplate;

import java.math.BigDecimal;
import java.util.*;
import java.util.concurrent.TimeUnit;

/**
 * @Description: 销项发票开具管理
 * @Author: jeecg-boot
 * @Date: 2023-07-30
 * @Version: V1.0
 */
@Service
@Transactional
@Slf4j
public class FpkjglServiceImpl implements IFpkjglService {

    private static final String REDIS_KEY_FPSQLSH = "fpkj:fpsqlsh:";
    @Autowired
    private IZhiyunSkfpService zhiyunSkfpService;
    @Autowired
    private IZhiyunSdfpService zhiyunSdfpService;
    @Autowired
    private ILeqiService leqiService;

    @Autowired
    private ICsXxDkdjService dkdjService;
    @Autowired
    private ICsXxDkdjExtService dkdjExtService;

    @Autowired
    private ICsXxDkdjmxService dkdjmxService;
    @Autowired
    private ICsXxDkdjCezsService dkdjCezsService;

    @Autowired
    private ICsPtTaskService taskService;
    @Autowired
    private ICsPtTaskmxService taskmxService;
    @Autowired
    private ApplicationContext applicationContext;
    @Autowired
    private ICsXxFpxxService fpxxService;
    @Autowired
    private ICsXxFpxxmxService fpxxmxService;
    @Autowired
    private ISysBaseAPI baseAPI;

    @Autowired
    private RedisTemplate<String, Object> redisTemplate;

    @Autowired
    private ICsXxYkdjService ykdjService;
    @Autowired
    private ICsXxKpzdService kpzdService;

    @Autowired
    private ICsXxProductService spxxService;
    @Autowired
    private ICsXxHzsqjsService csXxHzsqjsService;

    @Autowired
    private TransactionTemplate transactionTemplate;

    @Autowired
    private ICsXxDjtsjlService iCsXxDjtsjlService;

    @Autowired
    private ICsXxFwqxeService iCsXxFwqxeService;

    /***
     * 税控发票开具
     * 开具发票专票、普票、电子发票
     * @params 开票报文
     */
    public Result batchInvoice(BacthInvoiceParam params) {
        JSONArray ids = params.getIds();
        //设置异步任务
        CsPtTask task = TaskWorkUtils.getTask(TaskEnum.plkp.getDesc(), "批量开票任务", TaskStateEnum.dzx.getValue(), params.getIds().toJSONString());//保存信息
        task.setId(IdWorker.getIdStr());
        List<CsPtTaskmx> saveTaskMxs = new ArrayList();

        //任务明细:key：fromid+ywid
        Map<String, CsPtTaskmx> taskmxs = new HashMap<>();
        //待开单据信息组装
        Map<String, CsXxDkdjPage> dkdjPages = new HashMap<>();

        //更新待开单据为开票中
        List<CsXxDkdj> dkdjs = new ArrayList();

        //待开发票信息
        List<InvoiceParam> invoiceParamList = new ArrayList();

        //获取批量开票的单据信息
        ids.forEach(id -> {
            //获取批量开票的单据信息
            CsXxDkdj dkdj = dkdjService.getById((String) id);
            //查询待开单据明细
            List dkdjmxs = dkdjmxService.selectByMainId((String) id);

            //开票终端选择
            if (StringUtils.isNotEmpty(params.getKpzddm())) {
                dkdj.setKpzddm(params.getKpzddm());
            }

            //封装待开单据信息
            CsXxDkdjPage dkdjPage = new CsXxDkdjPage();
            BeanUtils.copyProperties(dkdj, dkdjPage);
            dkdjPage.setCsXxDkdjmxList(dkdjmxs);

            //取特定业务信息
            if (!StringUtils.isEmpty(dkdj.getTdyslxdm())) {
                List<CsXxDkdjExt> dkdjExtList = dkdjExtService.lambdaQuery().eq(CsXxDkdjExt::getFromid, id).list();
                if (dkdjExtList == null && dkdjExtList.size() > 0) {
                    CsXxDkdjExt xxDkdjExt = dkdjExtList.get(0);

                    if (TdyslxDmEnum.JZFW.getValue().equals(dkdj.getTdyslxdm())) {
                        Jzfw jzfw = JSON.parseObject(xxDkdjExt.getTdywInfo(), Jzfw.class);
                        dkdjPage.setTdywJzfw(jzfw);
                    } else if (TdyslxDmEnum.HWYSFW.getValue().equals(dkdj.getTdyslxdm())) {
                        List<Hwysfwmx> hwysfwmxList = JSONArray.parseArray(xxDkdjExt.getTdywInfo(), Hwysfwmx.class);
                        dkdjPage.setTdywHwysfwList(hwysfwmxList);
                    } else if (TdyslxDmEnum.BDCXS.getValue().equals(dkdj.getTdyslxdm())) {
                        Bdcxsfw bdcxsfw = JSON.parseObject(xxDkdjExt.getTdywInfo(), Bdcxsfw.class);
                        dkdjPage.setTdywBdcxs(bdcxsfw);
                    } else if (TdyslxDmEnum.BDCJYZLFW.getValue().equals(dkdj.getTdyslxdm())) {
                        Bdcjyzlfw bdcjyzlfw = JSON.parseObject(xxDkdjExt.getTdywInfo(), Bdcjyzlfw.class);
                        dkdjPage.setTdywBdcjyzlfw(bdcjyzlfw);
                    } else if (TdyslxDmEnum.LKYSFW.getValue().equals(dkdj.getTdyslxdm())) {
                        List<Lkysfwmx> lkysfwmxList = JSONArray.parseArray(xxDkdjExt.getTdywInfo(), Lkysfwmx.class);
                        dkdjPage.setTdywLkysfwList(lkysfwmxList);
                    }
                }
            }

            //取差额征收信息
            if (!ZsfsEnum.PTZS.getValue().equals(dkdj.getZsfs())) {
                //取差额信息
                List<CsXxDkdjCezs> csXxDkdjCezsList = dkdjCezsService.lambdaQuery().eq(CsXxDkdjCezs::getFromid, id).list();
                List<Cezs> cezsList = new ArrayList<>();
                csXxDkdjCezsList.forEach(csXxDkdjCezs -> {
                    Cezs cezs = new Cezs();
                    BeanUtils.copyProperties(csXxDkdjCezs, cezs);
                    cezsList.add(cezs);
                });
                dkdjPage.setCezsList(cezsList);
            }

            //将待开单据转换为待开票数据
            InvoiceParam dkfpParam = InvoiceConvertUtils.ConvertDjToFp(dkdjPage);

            invoiceParamList.add(dkfpParam);
            //将待开票单据修改为开票中状态
            dkdj.setKpzt(KpztEnum.kpz.getValue());
            dkdjs.add(dkdj);

            //设置任务明细
            CsPtTaskmx taskmx = new CsPtTaskmx();
            taskmx.setState(TaskStateEnum.dzx.getValue());
            taskmx.setFromid(task.getId());
            taskmx.setTenantId(task.getTenantId());
            taskmx.setYwid(dkdj.getId());
            taskmx.setYwtable(CsXxDkdj.class.getName());
            taskmx.setId(IdWorker.getIdStr());
            taskmx.setYwdesc(task.getNote());
            saveTaskMxs.add(taskmx);
            taskmxs.put(taskmx.getFromid() + taskmx.getYwid(), taskmx);

            dkdjPages.put(dkfpParam.getFpsqlsh(), dkdjPage);
        });
        if (dkdjs.size() > 0) {
            //更新开票状态
            dkdjService.saveOrUpdateBatch(dkdjs);
            //添加到异步任务
            taskService.save(task);
            taskmxService.saveBatch(saveTaskMxs);

            //触发批量开票事件
            BatchInvoiceEventVo eventVo = new BatchInvoiceEventVo();
            eventVo.setTask(task);
            eventVo.setTaskmxs(taskmxs);
            eventVo.setInvoiceParamList(invoiceParamList);
            eventVo.setDkdjpages(dkdjPages);
            BatchInvoiceEvent batchInvoiceEvent = new BatchInvoiceEvent(eventVo);
            applicationContext.publishEvent(batchInvoiceEvent);
            //发送消息通知
            MessageDTO me = new MessageDTO();
            me.setFromUser(task.getCreateBy());
            me.setToUser(task.getCreateBy());
            me.setTitle("任务编号：" + task.getId() + "批量开票开始执行通知");
            me.setType(MessageTypeEnum.XT.getType());
            me.setContent("本次提交待开票单据数量：" + dkdjs.size() + "个,请稍后查询开具结果");
            baseAPI.sendSysAnnouncement(me);

            return Result.OK("开票中：任务编号:" + task.getId() + ",请稍后查询开具结果!");
        } else {
            return Result.OK("本次提交待开单据0个");
        }

    }


    /***
     * 税控发票开具
     * 开具发票专票、普票、电子发票
     * @params 开票报文
     */
    @Transactional
    public Result<InvoiceRtn.InvoiceRtnData> invoice(InvoiceParam invoiceParam) {
        final Result rtn = new Result();
        LoginUser loginUser = LoginUserUtil.getLoginUser();
        CommonParam commonParam = kpzdService.getCommonParamByKpzd(invoiceParam.getKpzddm());
        String fpxxId = "";
        InvoiceRtn rtnData = null;
        try {
            //完善开票终端、企业信息、服务器信息
            invoiceParam.setKpzd(commonParam.getKpzd());
            invoiceParam.setQyxx(commonParam.getQyxx());
            invoiceParam.setFwqxx(commonParam.getFwqxx());
            String fplxdm = invoiceParam.getFplxdm();

            //如果不是接口开票 判断开票限额
            if(!"jkkp".equals(invoiceParam.getSjlx())){
                //判断开票限额
                //判断公司
                QueryWrapper<CsXxFwqxe> queryWrapperByGs = new QueryWrapper<>();
                queryWrapperByGs.eq("company_id",commonParam.getKpzd().getCompanyId());
                queryWrapperByGs.eq("fplxdm",fplxdm);
                queryWrapperByGs.isNull("ygdm");
                List<CsXxFwqxe> CsXxFwqxeByGs = iCsXxFwqxeService.list(queryWrapperByGs);
                if(!CsXxFwqxeByGs.isEmpty()){
                    CsXxFwqxe csXxFwqxe = CsXxFwqxeByGs.get(0);
                    BigDecimal xe = csXxFwqxe.getFpxe();
                    BigDecimal hjje = invoiceParam.getHjje();
                    BigDecimal hjse = invoiceParam.getHjse();
                    BigDecimal jshj = hjje.add(hjse);
                    if(jshj.compareTo(xe) > 0){
                        throw new JeecgBootException("价税合计:" + jshj + ",超出公司开票限额:" + xe);
                    }
                }
                //判断开票员
                QueryWrapper<CsXxFwqxe> queryWrapperByUser = new QueryWrapper<>();
                queryWrapperByUser.eq("company_id",commonParam.getKpzd().getCompanyId());
                queryWrapperByUser.eq("fplxdm",fplxdm);
                queryWrapperByUser.eq("ygdm",loginUser.getUsername());
                List<CsXxFwqxe> CsXxFwqxeByUser = iCsXxFwqxeService.list(queryWrapperByUser);
                if(!CsXxFwqxeByUser.isEmpty()){
                    CsXxFwqxe csXxFwqxe = CsXxFwqxeByUser.get(0);
                    BigDecimal xe = csXxFwqxe.getFpxe();
                    BigDecimal hjje = invoiceParam.getHjje();
                    BigDecimal hjse = invoiceParam.getHjse();
                    BigDecimal jshj = hjje.add(hjse);
                    if(jshj.compareTo(xe) > 0){
                        throw new JeecgBootException("价税合计:" + jshj + ",超出个人开票限额:" + xe);
                    }
                }
            }

            if (SjlxEnum.SG.getValue().equals(invoiceParam.getSjlx()) && StringUtils.isEmpty(invoiceParam.getSjyt())) {
                invoiceParam.setSjyt(commonParam.getQyxx().getSsyt());
            }
            //清单标志处理
            if (!FplxdmEnum.sdzp.getValue().equals(invoiceParam.getFplxdm())
                    && !FplxdmEnum.sdpp.getValue().equals(invoiceParam.getFplxdm())
                    && !FplxdmEnum.dzpp.getValue().equals(invoiceParam.getFplxdm())
                    && !FplxdmEnum.dzzp.getValue().equals(invoiceParam.getFplxdm())
                    && (StringUtils.isEmpty(invoiceParam.getQdbz()) || QdbzEnum.N.getValue().equals(invoiceParam.getQdbz()))) {
                //纸质票，并且清单标志为空，判断明细行超过8行则为清单票
                if (invoiceParam.getFpmxs().size() > 8) {
                    invoiceParam.setQdbz(QdbzEnum.Y.getValue());
                } else {
                    invoiceParam.setQdbz(QdbzEnum.N.getValue());
                }
            }

            if (StringUtils.isBlank(invoiceParam.getKhdzdh())) {
                invoiceParam.setKhdzdh(StringUtils.trimToEmpty(invoiceParam.getKhdzdh()) + " " + StringUtils.trimToEmpty(invoiceParam.getKhdh()));
            }
            if (StringUtils.isBlank(invoiceParam.getKhyhzh())) {
                invoiceParam.setKhyhzh(StringUtils.trimToEmpty(invoiceParam.getKhyh()) + " " + StringUtils.trimToEmpty(invoiceParam.getKhzh()));
            }
            if (StringUtils.isBlank(invoiceParam.getXfdzdh())) {
                invoiceParam.setXfdzdh(StringUtils.trimToEmpty(invoiceParam.getXfdz()) + " " + StringUtils.trimToEmpty(invoiceParam.getXfdh()));
            }
            if (StringUtils.isBlank(invoiceParam.getXfyhzh())) {
                invoiceParam.setXfyhzh(StringUtils.trimToEmpty(invoiceParam.getXfyh()) + " " + StringUtils.trimToEmpty(invoiceParam.getXfzh()));
            }

            //处理商品明细
            int mxhh = 1;
            for (InvoicemxParam djmx : invoiceParam.getFpmxs()) {
                if (SjlxEnum.SG.getValue().equals(invoiceParam.getSjlx())) {
                    djmx.setMxhh(mxhh);
                    mxhh++;
                }
                //转换商品名称
                String spmc = djmx.getSpmc();
                if (!spmc.startsWith("*")) {//商品名称没有税收分类名称开头
                    String spdm = djmx.getSpdm();
                    String ssmc = djmx.getSsmc();
                    if (StringUtils.isEmpty(ssmc)) {
                        List<CsXxProduct> spxxs = spxxService.lambdaQuery().eq(CsXxProduct::getSpdm, spdm).list();
                        if (spxxs.size() > 0) {
                            CsXxProduct spxx = spxxs.get(0);
                            ssmc = spxx.getSpssjc();
                        }
                    }
                    spmc = "*" + ssmc + "*" + spmc;
                    djmx.setSpmc(spmc);
                }
            }

            //发票开具参数校验
            Result checkResult = InvoiceParamValid.checkInvoiceParam(invoiceParam);
            if (!checkResult.isSuccess()) {
                rtn.setSuccess(false);
                rtn.setCode(CommonConstant.SC_INTERNAL_SERVER_ERROR_500);
                rtn.setMessage(checkResult.getMessage());
                return rtn;
            }

            //校验重复
            boolean isRepeat = redisTemplate.opsForValue().setIfAbsent(REDIS_KEY_FPSQLSH + invoiceParam.getFpsqlsh(), invoiceParam.getFpsqlsh(), 5, TimeUnit.MINUTES);
            if (!isRepeat) {
                throw new JeecgBootException("该请求流水号已开票，请误重复操作");
            }

            //发票信息表中校验是否重复开具
            int count = fpxxService.lambdaQuery().eq(CsXxFpxx::getDjbh, invoiceParam.getFpsqlsh()).list().size();
            if (count > 0) {
                throw new JeecgBootException("该请求流水号已开票，请误重复操作");
            }

            //收购发票需要购、销方信息对调(正数开票对调，负数发票不对调)
            if (TspzEnum.NCPSG.getValue().equals(invoiceParam.getTspz()) && KplxEnum.ZSFP.getValue().equals(invoiceParam.getKplx())) {
                //销售方信息
                String xfmc = invoiceParam.getXfmc();
                String xfnsrsbh = invoiceParam.getXfsh();
                String xfdzdh = invoiceParam.getXfdz();
                String xfdz = invoiceParam.getXfdz();
                String xfdh = invoiceParam.getXfdh();
                String xfyhzh = invoiceParam.getXfyhzh();
                String xfyh = invoiceParam.getXfyh();
                String xfzh = invoiceParam.getXfzh();
                String xfgsdm = invoiceParam.getGsdm();

                //购方信息
                String gfmc = invoiceParam.getKhmc();
                String gfnsrsbh = invoiceParam.getKhsh();
                String gfdzdh = invoiceParam.getKhdzdh();
                String gfdz = invoiceParam.getKhdz();
                String gfdh = invoiceParam.getKhdh();
                String gfyhzh = invoiceParam.getKhyhzh();
                String gfyh = invoiceParam.getKhyh();
                String gfzh = invoiceParam.getKhyhzh();
                String gfgsdm = invoiceParam.getKhdm();

                //购销方对换
                invoiceParam.setXfmc(gfmc);
                invoiceParam.setXfsh(gfnsrsbh);
                invoiceParam.setXfdzdh(gfdzdh);
                invoiceParam.setXfdz(gfdz);
                invoiceParam.setXfdh(gfdh);
                invoiceParam.setXfyhzh(gfyhzh);
                invoiceParam.setXfyh(gfyh);
                invoiceParam.setXfzh(gfzh);
                invoiceParam.setGsdm(gfgsdm);

                invoiceParam.setKhmc(xfmc);
                invoiceParam.setKhsh(xfnsrsbh);
                invoiceParam.setKhdzdh(xfdzdh);
                invoiceParam.setKhdz(xfdz);
                invoiceParam.setKhdh(xfdh);
                invoiceParam.setKhyhzh(xfyhzh);
                invoiceParam.setKhyh(xfyh);
                invoiceParam.setKhzh(xfzh);
                invoiceParam.setKhdm(xfgsdm);
            }

            //数电发票开具处理
            if (fplxdm.equals(FplxdmEnum.sdpp.getValue()) || fplxdm.equals(FplxdmEnum.sdzp.getValue())) {
                CsXxKpzd csXxKpzd = invoiceParam.getKpzd();
                if(KpzdkpmsEnum.lqtd.getValue().equals(csXxKpzd.getKpms())){
                    //乐企
                    rtnData = leqiService.invoice(invoiceParam);
                } else {
                    //数电
                    rtnData = zhiyunSdfpService.invoice(invoiceParam);
                }

            } else {
                rtnData = zhiyunSkfpService.invoice(invoiceParam);
            }

            if (rtnData.getSuccess()) {
                try {

                    rtn.setSuccess(true);
                    rtn.setCode(CommonConstant.SC_OK_200);
                    rtn.setMessage("开票成功");
                    rtn.setResult(rtnData.getData());

                    //取组织机构编码
                    if(StringUtils.isBlank(invoiceParam.getBmdm())){
                        LoginUser user = (LoginUser) SecurityUtils.getSubject().getPrincipal();
                        if (user == null) {
                            //取开票终端对应企业所在的组织机构代码
                            invoiceParam.setBmdm(invoiceParam.getQyxx().getZzjgOrgCode());
                        } else {
                            invoiceParam.setBmdm(user.getOrgCode());
                        }
                    }
                    //将开票信息保存到已开票单据和发票信息表中
                    //保存到发票信息表
                    fpxxId = fpxxService.saveFpxxByDkdj(invoiceParam, rtnData);
                    rtnData.getData().setFpxxId(fpxxId);

                    //将单据保存到已开单据表中
                    boolean isDelDkdj = false;
                    if (!StringUtils.isEmpty(invoiceParam.getYwdjbh())) {
                        isDelDkdj = true;
                    }

                    //手工开具负数发票时，不保存已开单据信息，其他情况将待开单据转到已开单据表中
                    if (!(KplxEnum.FSFP.getValue().equals(invoiceParam.getKplx()) && SjlxEnum.SG.getValue().equals(invoiceParam.getSjlx()))) {
                        ykdjService.saveYkdjByDkdjAndDelDkdj(invoiceParam, rtnData, isDelDkdj);
                    }
                } catch (Exception e) {
                    log.error(e.getMessage(), e);
                    StringBuffer msg = new StringBuffer();
                    msg.append("开票成功，后台处理异常！");
                    if (!FplxdmEnum.sdzp.getValue().equals(fplxdm) && !FplxdmEnum.sdpp.getValue().equals(fplxdm)) {
                        msg.append("发票代码：[" + rtnData.getData().getFpdm()).append("]");
                    }
                    msg.append("发票号码：[" + rtnData.getData().getFphm()).append("]");
                    log.error(msg.toString());

                    throw new JeecgBootException(msg.toString());
                    //return Result.error(msg.toString());
                }
            } else {
                rtn.setCode(Integer.parseInt(rtnData.getCode() == null ? "500" : rtnData.getCode()));
                rtn.setSuccess(false);
                rtn.setMessage(rtnData.getMsg());

                redisTemplate.delete(REDIS_KEY_FPSQLSH + invoiceParam.getFpsqlsh());
            }
            return rtn;
        } catch (Exception e) {
            log.error(e.getMessage(), e);
            redisTemplate.delete(REDIS_KEY_FPSQLSH + invoiceParam.getFpsqlsh());
            rtn.setCode(500);
            rtn.setSuccess(false);
            rtn.setMessage("开票失败：" + e.getMessage());

            throw new JeecgBootException(rtn.getMessage());
        } finally {
            try {
                //开启新事务
                transactionTemplate.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);
                transactionTemplate.execute(transactionStatus -> {
                    //负数发票更新红字信息表开具状态
                    if (KplxEnum.FSFP.getValue().equals(invoiceParam.getKplx()) && !StringUtils.isEmpty(invoiceParam.getHzxxbbh())) {
                        List<CsXxHzsqjs> hzsqjsList = csXxHzsqjsService.lambdaQuery()
                                .eq(CsXxHzsqjs::getXxbbh, invoiceParam.getHzxxbbh())
                                .orderByDesc(CsXxHzsqjs::getCreateTime).list();
                        if (hzsqjsList.size() > 0) {
                            CsXxHzsqjs csXxHzsqjs = hzsqjsList.get(0);
                            String kjzt = rtn.isSuccess() ? KpztEnum.ykp.getValue() : KpztEnum.kpsb.getValue();
                            csXxHzsqjs.setKjzt(kjzt);

                            csXxHzsqjsService.updateById(csXxHzsqjs);
                        }
                    }

                    //开票失败更新待开单据开具状态
                    if (!StringUtils.isEmpty(invoiceParam.getYwdjbh()) && !rtn.isSuccess()) {
                        dkdjService.updateDkdjKjzt(invoiceParam, rtn);
                    }
                    //jkkp已经保存过记录
                    if(!"jkkp".equals(invoiceParam.getSjlx())){
                        //保存开票信息 yangzheng
                        CsXxDjtsjlDo csXxDjtsjlDo = new CsXxDjtsjlDo();
                        csXxDjtsjlDo.setId(IdWorker.getIdStr());
                        csXxDjtsjlDo.setBody(JSONObject.toJSONString(invoiceParam));
                        csXxDjtsjlDo.setStatus(rtn.isSuccess() ? "0" : "2");
                        csXxDjtsjlDo.setTenantId(commonParam.getQyxx().getTenantId().toString());
                        csXxDjtsjlDo.setCompanyId(commonParam.getKpzd().getCompanyId());
                        csXxDjtsjlDo.setSysOrgCode(commonParam.getQyxx().getSysOrgCode());
                        csXxDjtsjlDo.setDjlx("sgfpkj");
                        csXxDjtsjlDo.setMessage(rtn.getMessage());
                        iCsXxDjtsjlService.save(csXxDjtsjlDo);
                    }
                    return Boolean.TRUE;
                });

                if(rtn.isSuccess()){
                    //下载版式文件
                    if (FplxdmEnum.sdzp.getValue().equals(invoiceParam.getFplxdm())
                            || FplxdmEnum.sdpp.getValue().equals(invoiceParam.getFplxdm())
                            || FplxdmEnum.dzpp.getValue().equals(invoiceParam.getFplxdm())
                            || FplxdmEnum.dzzp.getValue().equals(invoiceParam.getFplxdm())) {
                        FpDownloadEventVo eventVo = new FpDownloadEventVo();
                        eventVo.setId(fpxxId);
                        eventVo.setInvoiceParam(invoiceParam);
                        eventVo.setInvoiceRtn(rtnData);
                        FpDownloadEvent fpDownloadEvent = new FpDownloadEvent(eventVo);
                        applicationContext.publishEvent(fpDownloadEvent);
                    }
                    //触发回填逻辑
                    if (StringUtils.isNotEmpty(invoiceParam.getYwdjbh())) {
                        String id = invoiceParam.getFpsqlsh();
                        FphtEvent fphtEvent = new FphtEvent(id);
                        applicationContext.publishEvent(fphtEvent);
                    }
                }

            } catch (Exception e) {
                log.error(e.getMessage(), e);
            }
        }
        //return rtn;
    }

    /***
     * 已开发票作废
     * @params 作废报文
     */
    public Result<List<YkfpzfRtn>> ykfpzf(List<String> ids) {
        Result rtn = new Result();
        List<YkfpzfRtn> rtnList = new ArrayList<>();
        String msg = "";

        LoginUser user = (LoginUser) SecurityUtils.getSubject().getPrincipal();

        for (String id : ids) {
            YkfpzfRtn ykfpzfRtn = new YkfpzfRtn();

            CsXxFpxx csXxFpxx = fpxxService.getById(id);
            if (csXxFpxx == null) {
                ykfpzfRtn.setId(id);
                ykfpzfRtn.setZfzt(false);
                ykfpzfRtn.setMessage("发票信息不存在");
                rtnList.add(ykfpzfRtn);
                continue;
            }
            try {

                CommonParam commonParam = kpzdService.getCommonParamByKpzd(csXxFpxx.getKpzddm());

                //校验是否可作废
                if (FpztEnum.zf.getValue().equals(csXxFpxx.getFpzt())) {
                    ykfpzfRtn.setId(id);
                    ykfpzfRtn.setZfzt(false);
                    ykfpzfRtn.setFphm(csXxFpxx.getFphm());
                    ykfpzfRtn.setMessage("该发票已作废");
                    rtnList.add(ykfpzfRtn);
                    continue;
                }
                if (FplxdmEnum.sdzp.getValue().equals(csXxFpxx.getFplxdm())
                        || FplxdmEnum.sdpp.getValue().equals(csXxFpxx.getFplxdm())
                        || FplxdmEnum.dzpp.getValue().equals(csXxFpxx.getFplxdm())
                        || FplxdmEnum.dzzp.getValue().equals(csXxFpxx.getFplxdm())) {
                    ykfpzfRtn.setId(id);
                    ykfpzfRtn.setZfzt(false);
                    ykfpzfRtn.setFphm(csXxFpxx.getFphm());
                    ykfpzfRtn.setMessage("发票类型[" + csXxFpxx.getFplxmc() + "]暂不支持发票作废！");
                    rtnList.add(ykfpzfRtn);
                    continue;
                }
                //调接口
                FpzfParam params = new FpzfParam();
                //完善开票终端、企业信息、服务器信息
                params.setKpzd(commonParam.getKpzd());
                params.setQyxx(commonParam.getQyxx());
                params.setFwqxx(commonParam.getFwqxx());

                params.setKpzddm(csXxFpxx.getKpzddm());
                params.setFplxdm(csXxFpxx.getFplxdm());
                params.setZflx("1");
                params.setFpdm(csXxFpxx.getFpdm());
                params.setFphm(csXxFpxx.getFphm());
                params.setHjje(csXxFpxx.getHjje().toPlainString());
                params.setZfr(user.getRealname());

                FpzfRtn rtnData = zhiyunSkfpService.fpzf(params);
                if (rtnData.getSuccess()) {
                    FpzfRtn.FpzfRtnData fpzfRtnData = rtnData.getData();
                    //修改发票信息表发票状态
                    CsXxFpxx updateFpxx = new CsXxFpxx();
                    updateFpxx.setId(csXxFpxx.getId());
                    updateFpxx.setFpzt(FpztEnum.zf.getValue());
                    updateFpxx.setZfr(user.getUsername());
                    updateFpxx.setZfrq(fpzfRtnData.getZfrq());
                    fpxxService.updateById(updateFpxx);

                    ykfpzfRtn.setId(csXxFpxx.getId());
                    ykfpzfRtn.setZfzt(true);
                    ykfpzfRtn.setFphm(csXxFpxx.getFphm());
                    ykfpzfRtn.setMessage("作废成功");
                    rtnList.add(ykfpzfRtn);
                } else {
                    ykfpzfRtn.setId(csXxFpxx.getId());
                    ykfpzfRtn.setZfzt(false);
                    ykfpzfRtn.setMessage("作废失败：" + rtnData.getMsg());
                    ykfpzfRtn.setFphm(csXxFpxx.getFphm());
                    rtnList.add(ykfpzfRtn);
                }
            } catch (Exception e) {
                ykfpzfRtn.setId(id);
                ykfpzfRtn.setZfzt(false);
                ykfpzfRtn.setFphm(csXxFpxx.getFphm());
                ykfpzfRtn.setMessage("作废失败");
                rtnList.add(ykfpzfRtn);
            }
        }
        return Result.OK(rtnList);
    }

    @Override
    public YkfpzfRtn ykfpzf(FpzfDto dto) {
        YkfpzfRtn ykfpzfRtn = new YkfpzfRtn();
        CommonParam commonParam = kpzdService.getCommonParamByKpzd(dto.getKpzddm());
        CsXxFpxx csXxFpxx = fpxxService.lambdaQuery().eq(CsXxFpxx::getFpdm, dto.getFpdm()).eq(CsXxFpxx::getFphm, dto.getFphm()).one();
        if (csXxFpxx == null) {
            ykfpzfRtn.setZfzt(false);
            ykfpzfRtn.setMessage("发票信息不存在");
            ykfpzfRtn.setFphm(dto.getFphm());
            return ykfpzfRtn;
        }
        try {
            //校验是否可作废
            if (FpztEnum.zf.getValue().equals(csXxFpxx.getFpzt())) {
                ykfpzfRtn.setZfzt(false);
                ykfpzfRtn.setFphm(csXxFpxx.getFphm());
                ykfpzfRtn.setMessage("该发票已作废");
                return ykfpzfRtn;
            }
            if (FplxdmEnum.sdzp.getValue().equals(csXxFpxx.getFplxdm())
                    || FplxdmEnum.sdpp.getValue().equals(csXxFpxx.getFplxdm())
                    || FplxdmEnum.dzpp.getValue().equals(csXxFpxx.getFplxdm())
                    || FplxdmEnum.dzzp.getValue().equals(csXxFpxx.getFplxdm())) {
                ykfpzfRtn.setZfzt(false);
                ykfpzfRtn.setFphm(csXxFpxx.getFphm());
                ykfpzfRtn.setMessage("发票类型[" + csXxFpxx.getFplxmc() + "]暂不支持发票作废！");
                return ykfpzfRtn;
            }

            //调接口
            FpzfParam params = new FpzfParam();
            //完善开票终端、企业信息、服务器信息
            params.setKpzd(commonParam.getKpzd());
            params.setQyxx(commonParam.getQyxx());
            params.setFwqxx(commonParam.getFwqxx());
            params.setKpzddm(csXxFpxx.getKpzddm());
            params.setFplxdm(csXxFpxx.getFplxdm());
            params.setZflx("1");
            params.setFpdm(csXxFpxx.getFpdm());
            params.setFphm(csXxFpxx.getFphm());
            params.setHjje(csXxFpxx.getHjje().toPlainString());
            params.setZfr(dto.getZfr());
            FpzfRtn rtnData = zhiyunSkfpService.fpzf(params);
            if (rtnData.getSuccess()) {
                FpzfRtn.FpzfRtnData fpzfRtnData = rtnData.getData();
                //修改发票信息表发票状态
                CsXxFpxx updateFpxx = new CsXxFpxx();
                updateFpxx.setId(csXxFpxx.getId());
                updateFpxx.setFpzt(FpztEnum.zf.getValue());
                updateFpxx.setZfr(dto.getZfr());
                updateFpxx.setZfrq(fpzfRtnData.getZfrq());
                fpxxService.updateById(updateFpxx);

                ykfpzfRtn.setZfzt(true);
                ykfpzfRtn.setFphm(csXxFpxx.getFphm());
                ykfpzfRtn.setMessage("作废成功");
                return ykfpzfRtn;
            } else {
                ykfpzfRtn.setZfzt(false);
                ykfpzfRtn.setMessage("作废失败：" + rtnData.getMsg());
                ykfpzfRtn.setFphm(csXxFpxx.getFphm());
                return ykfpzfRtn;
            }
        } catch (Exception e) {
            ykfpzfRtn.setZfzt(false);
            ykfpzfRtn.setMessage("作废失败：" + e.getMessage());
            ykfpzfRtn.setFphm(dto.getFphm());
            return ykfpzfRtn;
        }
    }

    /**
     * 批量红冲
     *
     * @param fphcPage
     * @return
     */
    @Override
    public Result<List<FphcPLRtn>> plhc(FphcPLPage fphcPage) {
        List<FphcPLRtn> rtnList = new ArrayList<>();
        String msg = "";

        //取终端信息
        CsPtQyxx qyxx = LoginUserUtil.getQyxxByLoginUser();
        List<CsXxKpzd> kpzds = LoginUserUtil.getKpzdsByUserKpzdsq(KpzdlxEnum.skzd.getValue());
        CsXxKpzd kpzd = null;
        if (kpzds == null) {
            msg = "当前用户暂未分配" + qyxx.getNsrmc() + "开票终端，请先进行开票终端授权";
            return Result.error(msg);
        }
        kpzd = kpzds.get(0);

        CommonParam commonParam = kpzdService.getCommonParamByKpzd(kpzd.getZddm());

        String hcyydm = fphcPage.getHcyydm();
        List<String> ids = fphcPage.getIds();
        for (String id : ids) {
            FphcPLRtn fphcPLRtn = new FphcPLRtn();

            CsXxFpxx yFpxx = fpxxService.getById(id);//原发票信息
            if (yFpxx == null) {
                fphcPLRtn.setId(id);
                fphcPLRtn.setHczt(false);
                fphcPLRtn.setMessage("原发票信息不存在");
                rtnList.add(fphcPLRtn);
                continue;
            }

            List<CsXxFpxxmx> fpmxList = fpxxmxService.selectByMainId(id);
            try {
                //校验是否可红冲
                if (!FpztEnum.zc.getValue().equals(yFpxx.getFpzt())) {
                    fphcPLRtn.setId(id);
                    fphcPLRtn.setHczt(false);
                    fphcPLRtn.setFphm(yFpxx.getFphm());
                    fphcPLRtn.setMessage("该发票已红冲或已作废！");
                    rtnList.add(fphcPLRtn);
                    continue;
                }
                if (FplxdmEnum.zzszp.getValue().equals(yFpxx.getFplxdm())
                        || FplxdmEnum.dzzp.getValue().equals(yFpxx.getFplxdm())) {
                    fphcPLRtn.setId(id);
                    fphcPLRtn.setHczt(false);
                    fphcPLRtn.setFphm(yFpxx.getFphm());
                    fphcPLRtn.setMessage("专票不支持批量红冲！");
                    rtnList.add(fphcPLRtn);
                    continue;
                }
                if (FplxdmEnum.sdpp.getValue().equals(yFpxx.getFplxdm())
                        || FplxdmEnum.sdzp.getValue().equals(yFpxx.getFplxdm())) {
                    fphcPLRtn.setId(id);
                    fphcPLRtn.setHczt(false);
                    fphcPLRtn.setFphm(yFpxx.getFphm());
                    fphcPLRtn.setMessage("数电发票暂不支持直接红冲！");
                    rtnList.add(fphcPLRtn);
                    continue;
                }
                if (Arrays.asList(FplxdmEnum.zzspp.getValue(), FplxdmEnum.dzpp.getValue()).contains(yFpxx.getFplxdm()) && StringUtils.isEmpty(hcyydm)) {
                    fphcPLRtn.setId(id);
                    fphcPLRtn.setHczt(false);
                    fphcPLRtn.setFphm(yFpxx.getFphm());
                    fphcPLRtn.setMessage("红冲原因不能为空！");
                    rtnList.add(fphcPLRtn);
                    continue;
                }
                //封装参数
                InvoiceParam params = new InvoiceParam();
                BeanUtils.copyProperties(yFpxx, params);
                //完善开票终端、企业信息、服务器信息
                params.setKpzd(commonParam.getKpzd());
                params.setQyxx(commonParam.getQyxx());
                params.setFwqxx(commonParam.getFwqxx());
                params.setKpzddm(kpzd.getZddm());

                params.setSjlx(SjlxEnum.SG.getValue());//通过系统批量红冲时，数据类型设为手工
                params.setKplx(KplxEnum.FSFP.getValue());//负数发票
                params.setHcyy(hcyydm);//红冲原因
                params.setYfpdm(yFpxx.getFpdm());
                params.setYfphm(yFpxx.getFphm());
                params.setYdjbh(yFpxx.getDjbh());
                params.setFpsqlsh(IdWorker.getIdStr());//流水号

                List<InvoicemxParam> invoicemxParamList = new ArrayList<>();
                fpmxList.forEach(djmx -> {
                    InvoicemxParam invoicemxParam = new InvoicemxParam();
                    BeanUtils.copyProperties(djmx, invoicemxParam);
                    invoicemxParamList.add((invoicemxParam));
                });
                params.setFpmxs(invoicemxParamList);

                //负数发票金额、折扣行等处理
                params = FPUtil.invoiceInfoDiscountProcess(params);

                //处理备注
                String bz = params.getBz();
                if (StringUtils.isEmpty(bz) || !bz.contains("对应正数发票代码")) {
                    bz = "对应正数发票代码:" + StringUtils.trimToEmpty(params.getYfpdm()) + " 号码:" + StringUtils.trimToEmpty(params.getYfphm()) + "\n" + bz;
                    params.setBz(bz);
                }

                //调开具接口
                Result result = this.invoice(params);

                if (result.isSuccess()) {
                    fphcPLRtn.setId(yFpxx.getId());
                    fphcPLRtn.setHczt(true);
                    fphcPLRtn.setFphm(yFpxx.getFphm());
                    fphcPLRtn.setMessage("红冲成功");
                    rtnList.add(fphcPLRtn);
                } else {
                    fphcPLRtn.setId(yFpxx.getId());
                    fphcPLRtn.setHczt(false);
                    fphcPLRtn.setMessage("红冲失败：" + result.getMessage());
                    fphcPLRtn.setFphm(yFpxx.getFphm());
                    rtnList.add(fphcPLRtn);
                }
            } catch (Exception e) {
                fphcPLRtn.setId(id);
                fphcPLRtn.setHczt(false);
                fphcPLRtn.setFphm(yFpxx.getFphm());
                fphcPLRtn.setMessage("红冲失败");
                rtnList.add(fphcPLRtn);
            }
        }
        return Result.OK(rtnList);
    }


}
